import re
import bpy
from typing import Union
from bpy.types import Collection, ParticleSystem, Object, Context
from .basics import set_active_object
from ..functions.get_common_vars import get_common_vars
from ...addon.naming import FluidLabNaming


def create_particle_system(
                            context, ob, ps_name,
                            # emission:
                            emit_from: str = 'VOLUME',
                            ps_count: int = 20,
                            frame_start: Union[float, int] = 1,
                            frame_end: Union[float, int] = 250,
                            lifetime: int = 250,
                            lifetime_random: float = 0,
                            # emission source:
                            distribution: str = 'RAND',
                            grid_resolution: int = 10,
                            use_modifier_stack: bool = False,
                            # render
                            render_type: str = 'HALO',
                            particle_size: float = 0.1,
                            size_random: float = -1,
                            # render collection:
                            instance_collection: Collection = None,
                            # render extras:
                            show_unborn: bool = False, 
                            use_dead: bool = False,
                            # display viewport:
                            display_color: str = 'MATERIAL',
                            color_maximum: float = 1.0,
                            display_size: float = 0.035,
                            # physics:
                            physics_type: str = 'NEWTON',
                            mass: float = 1,
                            use_multiply_size_mass: bool = False,
                            stiffness: float = 0.5,
                            drag_factor: float = 0,
                            damping: float = 0,
                            use_size_deflect: bool = False,
                            integrator: str = 'MIDPOINT',
                            subframes: int = 0,
                            stiff_viscosity: float = 1.0,
                            spring_force: float = 0,
                            linear_viscosity: float = 0.5,
                            # velocity:
                            normal_factor: float = -1,
                            factor_random: float = -1,
                            # rotation:
                            use_rotations: bool = False,
                            phase_factor_random: float = -1,
                            use_dynamic_rotation: bool = False,
                            vertex_group_density: str = None,
) -> ParticleSystem:

    # buscamos si ya tiene ese sistema de particulas:
    psys = None
    ps_mods = [mod for mod in ob.modifiers if mod.type == 'PARTICLE_SYSTEM' and mod.name == ps_name]
    if ps_mods:
        psys = ps_mods[-1].particle_system

    # si no lo tiene se crea:
    if psys is None:
        # Crea un nuevo sistema de partículas
        ps_mod = ob.modifiers.new(ps_name, type='PARTICLE_SYSTEM')
        psys = ps_mod.particle_system

    # Accede a la configuración del sistema actualmente seleccionado
    settings = psys.settings

    settings.name = ps_name
    settings.emit_from = emit_from
    settings.count = ps_count
    settings.frame_start = frame_start
    settings.frame_end = frame_end
    settings.lifetime = lifetime
    settings.lifetime_random = lifetime_random


    settings.display_color = display_color
    settings.color_maximum = color_maximum

    settings.distribution = distribution
    settings.grid_resolution = grid_resolution
    settings.use_modifier_stack = use_modifier_stack

    settings.use_rotations = use_rotations
    settings.use_dynamic_rotation = use_dynamic_rotation
    
    settings.physics_type = physics_type
    settings.mass = mass
    settings.use_multiply_size_mass = use_multiply_size_mass
    settings.fluid.stiffness = stiffness 
    settings.drag_factor = drag_factor
    settings.damping = damping
    settings.use_size_deflect = use_size_deflect
    settings.integrator = integrator
    settings.subframes = subframes
    
    settings.fluid.stiff_viscosity = stiff_viscosity

    settings.render_type = render_type
    settings.show_unborn = show_unborn
    settings.use_dead = use_dead

    settings.fluid.spring_force = spring_force
    settings.fluid.linear_viscosity = linear_viscosity
    

    if normal_factor != -1:
        settings.normal_factor = normal_factor

    if factor_random != -1:
        settings.factor_random = factor_random

    if phase_factor_random != -1:
        settings.phase_factor_random = phase_factor_random

    if size_random != -1:
        settings.size_random = size_random

    if instance_collection:
        settings.instance_collection = instance_collection

    # random seed:
    psys.seed = list(context.view_layer.objects).index(ob)

    if vertex_group_density:
        psys.vertex_group_density = vertex_group_density
    
    # Caso especial:
    settings.particle_size = particle_size
    settings.display_size = display_size
    # print(f"Creamos particulas con particle_size: {particle_size}")
    # print(f"Creamos particulas con display_size: {display_size}")
    
    return psys


def particle_system_remove(context, ob:Object, ps_name: str, settings_name: str, names: str ='EXACT') -> None:
    
    if ob.name in context.view_layer.objects:

        for mod in ob.modifiers:
            
            if mod.type != 'PARTICLE_SYSTEM':
                continue

            if names == 'EXACT':
            
                if ps_name != mod.particle_system.name:
                    continue

                if ps_name != mod.particle_system.settings.name:
                    continue
            
            elif names == 'START_WITH':

                if not mod.particle_system.name.startswith(ps_name):
                    continue

                if not mod.particle_system.settings.name.startswith(ps_name):
                    continue


            ob.modifiers.remove(mod)


def copy_particle_system_to_selected_objects(context, ps_name: str) -> None:
    if ps_name in bpy.data.particles:
        ao = context.active_object
        for ob in context.selected_objects:
            if ob == ao:
                continue
            set_active_object(context, ob)
            bpy.ops.object.particle_system_add()
            ob.particle_systems[-1].name = ps_name
            ps = ao.particle_systems.get(ps_name)
            ob.particle_systems[-1].settings = ps.settings


@staticmethod
def selected_objects_ps_update_settings(self, context, ps_name: str, target_attr: str) -> None:
    for ob in context.selected_objects:
        for ps in ob.particle_systems:
            if not ps.name.startswith(ps_name):
                continue
            setattr(ps.settings, target_attr, getattr(self, target_attr))



def get_particle_count(context, ob:Object) -> int:
    # Dependancy graph
    degp = context.evaluated_depsgraph_get()

    # Evaluate the depsgraph (Important step)
    particle_systems = ob.evaluated_get(degp).particle_systems

    # All particles of first particle-system which has index "0"
    psys = particle_systems.active

    # Total Particles:
    return len(psys.particles) if hasattr(psys, "particles") else 0